/************************************************************************************
**  
* @copyright (c) 2015-, Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file     duye_cap_engine.cpp
* @version     
* @brief      
* @author   
* @date     2013-01-14
* @note 
*
*  1. 2018-01-14 created this file
* 
*/
#include <set>
#include <duye_system.h>
#include <duye_logger.h>
#include <duye_library.h>
#include <duye_if_dev.h>
#include <duye_cap_engine.h>
#include <duye_cap_linklayer_service.h>
#include <duye_cap_netlayer_service.h>
#include <duye_cap_tcp_service.h>
#include <duye_cap_udp_service.h>
#include <duye_cap_udp_service.h>
#include <duye_cap_http_service.h>

static const int8* DUYE_LOG_PREFIX = "duye.cap.engine";

typedef void *(*CreateFun)(void);

namespace duye {

CapEngine::CapEngine() {}

CapEngine::~CapEngine() {
	PluginContainer::iterator iter = m_pluginContainer.begin();
	for (; iter != m_pluginContainer.end(); ++iter) {
		delete iter->second;
	}

	m_pluginContainer.clear();
}

bool CapEngine::start() {
	// startup service
	if (!startupService()) {
		return false;
	}
		
	return true;
}

bool CapEngine::stop() {
	// stop service
	DataServiceContainer::iterator iter = m_dataServiceContainer.begin();
	for (; iter != m_dataServiceContainer.end(); ++iter) {
		if (!iter->second->stop()) {
			DUYE_ERROR("stop service %s failed", iter->second->name().c_str());
		}
	}
	
	return true;
}

bool CapEngine::run() {
	for (;;) {
		DUYE_INFO("CapEngine Working");
		System::sleep(10);
	}

	return true;
}

bool CapEngine::addPlugin(const std::string& pluginName, const std::string& pluginPath, ModuleTransfer* transfer) {
	PluginInfo* pluginInfo = new PluginInfo;

	// load plugin
	if (!loadPlugin(pluginName, pluginPath, *pluginInfo)) {
		DUYE_ERROR("load plugin '%s' failed", pluginName.c_str());
		delete pluginInfo;
		return false;
	}

	AppPlugin* pluginObj = dynamic_cast<AppPlugin*>(pluginInfo->object());
	if (pluginObj == NULL) {
		delete pluginInfo;
		return false;
	}

	// init plugin
	if (!pluginObj->onInit(transfer)) {
		delete pluginInfo;
		return false;
	}

	// get plugin filter rule
	FilterRule filterRule;
	pluginObj->onGetFilterRule(filterRule);
	pluginInfo->setFilterRule(filterRule);

	// get plugin needs data service list
	DataServiceTypeVector dataServiceList;
	pluginObj->onGetDataServiceList(dataServiceList);
	pluginInfo->setDataServiceList(dataServiceList);

	pluginInfo->setPath(pluginPath);
	
	m_pluginContainer.insert(std::make_pair(pluginName, pluginInfo));

	return true;
}

bool CapEngine::addPluginObj(const std::string& pluginName, AppPlugin* pluginObj, ModuleTransfer* transfer) {
	if (pluginObj == NULL) {
		return false;
	}

	PluginInfo* pluginInfo = new PluginInfo;
	// init plugin
	if (!pluginObj->onInit(transfer)) {
		delete pluginInfo;
		return false;
	}

	// get plugin filter rule
	FilterRule filterRule;
	pluginObj->onGetFilterRule(filterRule);
	pluginInfo->setFilterRule(filterRule);

	// get plugin needs data service list
	DataServiceTypeVector dataServiceList;
	pluginObj->onGetDataServiceList(dataServiceList);
	pluginInfo->setDataServiceList(dataServiceList);

	pluginInfo->setObject(pluginObj);

	m_pluginContainer.insert(std::make_pair(pluginName, pluginInfo));

	return true;
}

void CapEngine::delPlugin(const std::string& pluginName) {

	PluginContainer::iterator iter = m_pluginContainer.find(pluginName);
	if (iter == m_pluginContainer.end()) {
		return;
	}

	m_pluginContainer.erase(iter);
}

void CapEngine::addInterface(const NetIfDev& ifDev) {
	m_ifNameList.push_back(ifDev.getName());
	m_ifIpList.push_back(ifDev.getIP());
}

void CapEngine::addCapFile(const std::string& capFilePath) {
	m_capFileList.push_back(capFilePath);
}

std::string CapEngine::getCurrentIfIp() {
	if (m_ifIpList.empty()) {
		return "";
	}
	else {
		return *m_ifIpList.begin();
	}
}

bool CapEngine::loadPlugin(const std::string& pluginName, const std::string& pluginPath, PluginInfo& pluginInfo) {
	Library lib_loader;
	void* dllHandle = lib_loader.open(pluginPath.c_str());
	if (dllHandle == NULL) {
		DUYE_ERROR("sys::openLib('%s') failed", pluginPath.c_str());
		return false;
	}

	std::string funName = "Create" + pluginName;
	CreateFun function = (CreateFun)lib_loader.getSym(dllHandle, funName.c_str());
	if (function == NULL) {
		DUYE_ERROR("sys::loadLibSym('%s') failed", funName.c_str());
		lib_loader.close(dllHandle);
		return false;
	}

	CapObject* object = static_cast<CapObject*>(function());
	if (object == NULL) {
		DUYE_ERROR("plugin convert to failed", funName.c_str());
		lib_loader.close(dllHandle);
		return false;
	}
	
	pluginInfo.setDllHandle(dllHandle);
	pluginInfo.setObject(object);
		
	return true;
}

bool CapEngine::startupService() {
	// setting net card name
	if (!m_capFileList.empty()) {
		LinkLayerService::instance().setCapfile(*m_capFileList.begin());
	} else {
		LinkLayerService::instance().setNetcard(*m_ifNameList.begin());
	}

	// get needs service
	uint32 services[DS_MAX_NUM] = {0};
	PluginContainer::iterator iter = m_pluginContainer.begin();
	for (; iter != m_pluginContainer.end(); ++iter) {
		const DataServiceTypeVector& dataServiceList = iter->second->dataServiceList();
		DataServiceTypeVector::const_iterator dt_iter = dataServiceList.begin();
		for (; dt_iter != dataServiceList.end(); ++dt_iter) {
			services[*dt_iter]++;
			if (*dt_iter > DS_NETLAYER) {
				services[DS_LINKLAYER]++;
				services[DS_NETLAYER]++;
			}
		}
	}

	// create data service
	for (uint32 i = 0; i < DS_MAX_NUM; ++i) {
		if (services[i] > 0) {
			CapDataService* dataService = getDataService((DataServiceType)i);
			if (dataService == NULL) {
				continue;
			}

			m_dataServiceContainer.insert(std::make_pair((DataServiceType)i, dataService));
		}
	}

	// add plugin listener to data service
	iter = m_pluginContainer.begin(); 
	for (; iter != m_pluginContainer.end(); ++iter) {
		DataServiceListener* listenerObj = dynamic_cast<DataServiceListener*>(iter->second->object());
		if (listenerObj == NULL) {
			DUYE_WARN("plugin '%s' isn't a DataServiceListener type", iter->first.c_str());
			continue;
		}

		const DataServiceTypeVector& dataServiceList = iter->second->dataServiceList();
		const FilterRule* rule = NULL;
		if (iter->second->filterRule().hasFilterRule()) {
			rule = &(iter->second->filterRule());
		}

		DataServiceTypeVector::const_iterator dt_iter = dataServiceList.begin();
		for (; dt_iter != dataServiceList.end(); ++dt_iter) {			
			m_dataServiceContainer[*dt_iter]->addListener(listenerObj, rule);
		}
	}

	// startup service
	DataServiceContainer::iterator dsc_iter = m_dataServiceContainer.begin();
	for (; dsc_iter != m_dataServiceContainer.end(); ++dsc_iter) {
		if (!dsc_iter->second->start()) {
			DUYE_ERROR("startup service %s failed", dsc_iter->second->name().c_str());
			return false;
		}
	}

	return true;
}

CapDataService* CapEngine::getDataService(const DataServiceType type) {
	switch (type) {
	case DS_LINKLAYER:
		return &LinkLayerService::instance();
		break;
	case DS_NETLAYER:
		return &NetLayerService::instance();
		break;
	case DS_TCP:
		return &TcpService::instance();
		break;
	case DS_UDP:
		return &UdpService::instance();
		break;
	case DS_HTTP:
		return &HttpService::instance();
		break;
	default:
		return NULL;
		break;
	}
}

PluginInfo::PluginInfo() : m_dllHandle(NULL), m_object(NULL) {}
PluginInfo::~PluginInfo() {
	if (m_object != NULL) {
		delete m_object;
		m_object = NULL;
	}

	if (m_dllHandle != NULL) {
		//sys::closeLib(m_dllHandle);
		m_dllHandle = NULL;
	}
}

}
